#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <iostream>


#define lend endl
#define logit(x) cout 



#define HANDLER_MORE 1
#define HANDLER_FINISHED 2

using namespace std;

void printchararray(const char* a, unsigned int s, const char* heading){
        printf("__________%s (%d)___________\n", heading, s);
        for(int i=0;i<s;i++)
                printf("%c", a[i]);
        printf("__________\n");
}
/*************************************************************************
 * stristr
 *************************************************************************/
char* strnstrn(const char* haystack, const unsigned int haystacksize, const char* needle, const unsigned int needlesize){
        int i,j;
        short quit=1;
        int diff;

        if(!haystack || !needle || (diff=haystacksize-needlesize)<0){
                return 0;
        }

        for(i=0;i<=diff;i++){
                quit=0;
                for(j=0;j<needlesize;j++){
                        if(needle[j]!=haystack[i+j]){
                                quit=1;
                                break;
                        }
                }
                if(!quit){
                        return (char*)&haystack[i];
                }
        }
        return 0;
}




typedef struct Chunkt{
        char* data;
        int datalength;
	int actualdatalength;
        struct Chunkt* nextchunk;
}Chunk;

Chunk *currentChunk;
Chunk *chunks;
int currentchunkdatasize;
char* leftovers;
int leftoverslength;
unsigned int approxchunksize;


void show_chunks(Chunk* chunk){
        Chunk *t;
        for(t = chunk; t; t=t->nextchunk){
		printf("actu %d %d\n", t->actualdatalength, t->datalength);
		if(t->actualdatalength==-1)
			break;
		if(t->datalength==0)
                	printchararray(t->data, t->actualdatalength, "CHUNK Display");
		else
                	printchararray(t->data, t->actualdatalength-2, "CHUNK Display");
        }
}





void freechunks(){
        Chunk *oldchunk;
        while(chunks){
                printf("Chunk is still alive\n");
                oldchunk=chunks;
                chunks=chunks->nextchunk;
                if(oldchunk->data){
                       	free(oldchunk->data);
                       	oldchunk->data=0;
                }
                free(oldchunk);
        }

}

int flattenchunks(char** out, unsigned int* outsize, short x){
        Chunk *oldchunk;
        char* c, *tc;
        int l,  end;

                int tmpsl;
        l=0;
        c=0;
        
        end=(x?0:2);

	tmpsl=0;
        while(chunks){
                char tmps[64]={0};

		printf("CHUNKERS CHU 88 %d %d\n", chunks->datalength, chunks->actualdatalength);

		if(chunks->actualdatalength==-1){
			freechunks();
			break;
		}
		if(x){
                	sprintf(tmps, "%xRN",chunks->datalength-(chunks->datalength?2:0));
			tmpsl=strlen(tmps);
			if((tc=(char*)realloc(c, l+tmpsl))){
				c=tc;
                                memcpy(c+l, tmps, tmpsl);
				l+=tmpsl;
			}else{
				if(c)
					free(c);
				freechunks();
				return 1;
			}
		}
		if(chunks->datalength!=0){
         		if((tc=(char*)realloc(c, l+chunks->actualdatalength-end))){
				c=tc;
         	               	memcpy(c+l, chunks->data, chunks->actualdatalength-end);
         	               	l+=(chunks->actualdatalength-end);
         	       	}
         	       	else{
         	               if(c){
         	                       free(c);
         	               }
         	               freechunks();
         	               return 1;
         	       	}
		}
         	oldchunk=chunks;
         	chunks=chunks->nextchunk;
         	if(oldchunk->data)
         	        free(oldchunk->data);
         	free(oldchunk);
        }	
        *out=c;
        *outsize=l;
        return 0;
}


unsigned int getflattenedchunksize(Chunk* chunk){
        unsigned int total;
        Chunk* t;
        total=0;
        for(t = chunk; t; t=t->nextchunk){
                total+=(t->datalength-2);
        }
        return total;
}








//#define MAX_CHUNKDATA_SIZE 1250000
#define MAX_CHUNKDATA_SIZE 125
int decode_chunks(char** buf, int* bufsize){
        char* c, *b, *end;
        int s, chunking_fin;
	short maxedout=0;

        printchararray(*buf, *bufsize, "Pre CHUNK");
        chunking_fin=0;

        if(leftovers){
                logd<<"Chunked: first leftovers "<<leftoverslength<<lend;
                printchararray(leftovers, leftoverslength, "LEFTOVERS Pre");
                leftovers=(char*)realloc(leftovers,leftoverslength+ *bufsize);
                memcpy(leftovers+leftoverslength, *buf, *bufsize);
                s=*bufsize+leftoverslength;
                b=leftovers;
                leftoverslength+=*bufsize;
        }
        else{
                b=*buf;
                s=*bufsize;
        }
        
        printchararray(b, s, "Post CHUNK");
        
        while(s>0){
                printf ("s>0   %d>0\n", s);
                char tmpchunk[64]={0};
                if(currentchunkdatasize==-1 || maxedout){
                        if((c=strnstrn(b, s, "RN",2)) && !maxedout){
                                logd<<"Chunked: got \\r\\n"<<lend;
                                if(chunks==0){
                                        chunks = (Chunk*)malloc(sizeof(Chunk));
                                        currentChunk=chunks;
                                }
                                else{
                                        currentChunk->nextchunk=(Chunk*)malloc(sizeof(Chunk));
                                        currentChunk=currentChunk->nextchunk;
                                }

                                currentChunk->data=0;
                                currentchunkdatasize=0;
                                memcpy(tmpchunk, b, c-b);
                                currentChunk->nextchunk=0;

				currentChunk->actualdatalength=0;
                                currentChunk->datalength=strtol(tmpchunk, &end, 16);
                                if(currentChunk->datalength==0){
					chunking_fin=1;
                                	return HANDLER_FINISHED; 
				}
                               	else
					currentChunk->datalength+=2; //collect the \r\n at the end too

				approxchunksize+=currentChunk->datalength;

				if( approxchunksize > MAX_CHUNKDATA_SIZE){
                                	logd<<"Chunked: too much data to handle"<<lend; 
					maxedout=1;
					continue;
				}	


                                currentChunk->data=(char*)calloc(1,currentChunk->datalength);
                                logd<<"Chunked: allocating data length "<<currentChunk->datalength<<lend;
                                s=s-(c-b+2);
                                b=c+2;
                                //printf("h-decode %d %d\n", currentChunk->datalength, s);
                                leftoverslength=0;
                        }
                        else{
                         	logd<<"Chunked: leftovers"<<lend;                        
				if(leftoverslength==0){
					char* old = leftovers;
                                        leftovers=(char*)malloc(s);
                                        memcpy(leftovers,b,s);
                                        leftoverslength=s;
                                        printchararray(leftovers, leftoverslength, "LEFTOVERS");
                                        s=0;
                                        currentchunkdatasize=-1;
					if(old)
						free(old);
				}
				if(maxedout){
					currentChunk->actualdatalength=-1;
					printchararray(leftovers, leftoverslength, " MAxedout and quitting");
					return HANDLER_FINISHED;
				}
				else
                                	return HANDLER_MORE;
                        }
                }

                if(currentChunk->datalength-currentchunkdatasize<=s ){
                        memcpy(currentChunk->data+currentchunkdatasize, b, currentChunk->datalength-currentchunkdatasize);
                        if(currentChunk->datalength-currentchunkdatasize<s){
                                logd<<"Chunked: copying to data with remainder"<<lend;
                                b=b+(currentChunk->datalength-currentchunkdatasize);
                                s-=(currentChunk->datalength-currentchunkdatasize);
                        }
                        else{
                                logd<<"Chunked: copying to data in full\n"<<lend;
                                s=0;
                        }
                        currentchunkdatasize=-1;
                        printchararray(currentChunk->data, currentChunk->datalength, "CHUNKed data");
			currentChunk->actualdatalength=currentChunk->datalength;
                }
                else{
                        memcpy(currentChunk->data+currentchunkdatasize,b,s); 
                        currentchunkdatasize+=s;
			currentChunk->actualdatalength=currentchunkdatasize;
                        s=0;
                }
        }
        return HANDLER_MORE;
}


int main (int argv, char** argc){
        //char* hello="3RNoneRN5RNffourRN2RNtoRN0RN";int x = strlen(hello);
		
        char *x[6]={"7bR",
		"N<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">RNRN2dRN<html xmlns=\"http://www.w3.org/1999/xhtml\">RNRN2eRN<!-- #BeginTemplate \"../../simtecv4.dwt\" -->RNRN6",
                "R",
                "N<head>RN",
                "2RNtoRN",
                "0RN"};
       	
	/*
        char *x[4]={"7bRN",
		"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">RNRN2dRN<html xmlns=\"http://www.w3.org/1999/xhtml\">RNRN2eRN<!-- #BeginTemplate \"../../simtecv4.dwt\" -->RNRN6",
                "R",
                "N<he"};
       	*/ 
        currentChunk=0;
        chunks=0;
        currentchunkdatasize=-1;
        leftovers=0;
        leftoverslength=0;
	approxchunksize=0;

        char* out=0;
        unsigned int outsize=0;
	int i;

        printf("\n###############PARTIAL#################\n");
        for(i=0; i<100 ; i++){
                int cz=strlen(x[i]);
                char* cs=x[i];
                if(decode_chunks(&cs, &cz)==HANDLER_FINISHED)
                        break;
        }
        show_chunks(chunks);
	//flattenchunks(&out, &outsize, 1);
        flattenchunks(&out, &outsize, 0);
        printchararray(out, outsize, "OUT");
        printchararray(leftovers, leftoverslength, "OUT");

	free(out);
	if(leftovers)
		free(leftovers);
	freechunks();
	return 0;







        
        
        //flattenchunks(chunks, &out, &outsize, 0);

        
        //show_chunks(chunks);

        free(out);
        
        
        //printchararray(out, outsize, "OUT");
        //free(out);
}

